VueRouter
前端路由实现起来其实很简单,本质就是监听 URL 的变化,然后匹配路由规则,继而对不同的组件进行渲染,显示相应的页面,并且无须刷新。
有两种形式的路由:
- hash 模式
- history 模式. History 模式是 HTML5 新推出的功能,比之 Hash URL 更加美观
路由原理
hash 主要依赖 location.hash 来改动 URL, 达到不刷新跳转的效果.每次 hash 改变都会触发 hashchange 事件(来响应路由的变化,比如页面的更换) history 主要利用了 HTML5 的 historyAPI 来实现,用 pushState 和 replaceState 来操作浏览历史记录栈
hash
hash 出现在 URL 中,但不会被包含在 http 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
window.location.hash = route.fullPath
执行 router.push(url) 的操作的时候, 会执行 updateRoute 函数, 通过一个全局 mixins 会执行 this._route = route 这样一个操作。因为 _route 是响应式的,所以就会重新执行 render 函数 ?? (好像这逻辑有问题)
www.test.com/#/ 就是 Hash URL,当 # 后面的哈希值发生变化时,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面,并且无论哈希值如何变化,服务端接收到的 URL 请求永远是 www.test.com。
window.addEventListener("hashchange", () => {
// ... 具体逻辑
});
Hash 模式相对来说更简单,并且兼容性也更好。
history
主要是两个 api:
- pushState
- popState
执行 router.push(url) 的操作的时候,会执行 pushState(url) 这样的操作,直接更新一个状态。
History 模式是 HTML5 新推出的功能,主要使用 history.pushState 和 history.replaceState 改变 URL。这两个方法应用于浏览器记录栈,在当前已有的 back、forward、go 基础之上,它们提供了对历史记录修改的功能。只是当它们执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求。它们分别可以添加和修改历史记录条目。
通过 History 模式改变 URL 同样不会引起页面的刷新,只会更新浏览器的历史记录。
// 新增历史记录. pushState() 需要三个参数: 一个状态对象, 一个标题 (目前被忽略), 和 (可选的) 一个 URL.
history.pushState(stateObject, title, URL);
// 替换当前历史记录
history.replaceState(stateObject, title, URL);
当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件
window.addEventListener("popstate", (e) => {
// e.state 就是 pushState(stateObject) 中的 stateObject
console.log(e.state);
});
pushState API 传参的属性:
- 状态对象 — 状态对象 state 是一个 JavaScript 对象,通过 pushState () 创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate 事件就会被触发,且该事件的 state 属性包含该历史记录条目状态对象的副本。状态对象可以是能被序列化的任何东西。原因在于 Firefox 将状态对象保存在用户的磁盘上,以便在用户重启浏览器时使用,我们规定了状态对象在序列化表示后有 640k 的大小限制。如果你给 pushState() 方法传了一个序列化后大于 640k 的状态对象,该方法会抛出异常。如果你需要更大的空间,建议使用 sessionStorage 以及 localStorage.
- 标题 — Firefox 目前忽略这个参数,但未来可能会用到。传递一个空字符串在这里是安全的,而在将来这是不安全的。二选一的话,你可以为跳转的 state 传递一个短标题。
- URL — 该参数定义了新的历史 URL 记录。注意,调用 pushState() 后浏览器并不会立即加载这个 URL,但可能会在稍后某些情况下加载这个 URL,比如在用户重新打开浏览器时。新 URL 不必须为绝对路径。如果新 URL 是相对路径,那么它将被作为相对于当前 URL 处理。新 URL 必须与当前 URL 同源,否则 pushState() 会抛出一个异常。该参数是可选的,缺省为当前 URL。
两种模式对比
- Hash 模式只可以更改
#后面的内容,History 模式可以通过 API 设置任意的同源 URL - History 模式可以通过 API 添加任意类型的数据到历史记录中,Hash 模式只能更改哈希值,也就是字符串
- Hash 模式值不会被带到服务器上去(哈希路由的哈希值 nginx 不能收到。全部的操作、页面的变化都只存在于浏览器上。),无需后端配置,并且兼容性好。History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,后端需要配置
index.html页面用于匹配不到静态资源的时候 - 一个是 hash, 一个看起来像真实的路径
$route 和 $router 的区别
$route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。 $router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。
路由跳转
- 声明式(标签跳转)
<router-link :to="index"> - 编程式( js 跳转)
router.push('index')
导航钩子
三种
- 全局导航钩子
- router.beforeEach(to, from, next),
- router.beforeResolve(to, from, next),
- router.afterEach(to, from ,next)
- 组件内钩子
- beforeRouteEnter,
- beforeRouteUpdate,
- beforeRouteLeave
- 单独路由独享组件
- beforeEnter
首页可以控制导航跳转,beforeEach,afterEach 等,一般用于页面 title 的修改。一些需要登录才能调整页面的重定向功能。
- beforeEach 主要有 3 个参数 to,from,next:
- to:route 即将进入的目标路由对象,
- from:route 当前导航正要离开的路由
- next:function 一定要调用该方法 resolve 这个钩子。执行效果依赖 next 方法的调用参数。可以控制网页的跳转。
vue 怎么实现页面的权限控制
利用 vue-router 的 beforeEach 事件,可以在跳转页面前判断用户的权限(利用 cookie 或 token),是否能够进入此页面,如果不能则提示错误或重定向到其他页面,在后台管理系统中这种场景经常能遇到。
router 的全局守卫可以放一个全局鉴权。但是需要注意的是, App.vue 的 mounted 和 created 执行的额时机都要比守卫函数执行的早 ? App.vue 里面的逻辑应该有一部分不能被阻塞
router.beforeEach 能否 async
router 的权限
- 方案一: 维护一个统一的路由,根据用户的权限,过滤得到有权限的路由,只注册有权限的路由
- 方案二:注册全部的路由,通过 meta 属性以及 router 的守卫,每次进入路由之前去校验是否有权限。